Create a forbidden.js or forbidden.tsx file in your app directory that exports a default React component to display a custom 403 error UI
The forbidden.js file is a special Next.js file (experimental as of v15.1.0) that renders a user interface when the forbidden() function is called during authentication or authorization checks. When this file exists and the forbidden() function is invoked, Next.js will display your custom UI and automatically return a 403 HTTP status code. This allows you to create consistent, branded access-denied pages for users who are authenticated but lack sufficient permissions to access certain resources .
Enable experimental flag: In your next.config.js or next.config.ts, add experimental: { authInterrupts: true } to enable this feature
Create the file: Place forbidden.js or forbidden.tsx in your app directory (or within specific route segments like app/admin/forbidden.tsx for route-specific forbidden pages)
Export a default component: The file must export a default React component that renders your custom forbidden UI
No props needed: The forbidden component does not accept any props, unlike error.js which receives error and reset props
Invoke forbidden(): In your server components, server actions, or route handlers, import and call forbidden() from 'next/navigation' when access should be denied
The forbidden.js file works alongside the unauthorized.js file (for unauthenticated users) to provide comprehensive auth handling. While unauthorized() typically redirects to login flows, forbidden() is for authenticated users who lack specific permissions. You can place forbidden.tsx at different levels in your app directory - a root-level forbidden file handles all forbidden errors in the app, while segment-specific files (like app/dashboard/admin/forbidden.tsx) will only handle forbidden errors within that route segment, allowing for granular control over error presentations .
forbidden.js vs unauthorized.js: forbidden.js handles authenticated but insufficient permissions (403), while unauthorized.js handles unauthenticated users (401)
forbidden.js vs not-found.js: forbidden.js is for permission errors with explicit forbidden() calls, not-found.js handles missing resources automatically or via notFound() calls
forbidden.js vs error.js: forbidden.js doesn't receive props and is specifically for authorization failures, while error.js receives error objects and reset functions for general errors
The feature is currently experimental (introduced in v15.1.0) and subject to change, so it's not recommended for production use yet. However, you can try it out and provide feedback on GitHub. The forbidden() function cannot be called in the root layout, and when invoked in Server Components, it will render the nearest forbidden.js UI. In Server Actions or Route Handlers, it will return a 403 response and interrupt execution .